package com.fuershiye.woman.monitor.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.fuershiye.woman.monitor.annotations.AttributesType;
import com.fuershiye.woman.monitor.annotations.QueryCollection;
import com.fuershiye.woman.monitor.annotations.QueryEntry;
import com.fuershiye.woman.monitor.annotations.QueryField;
import com.fuershiye.woman.monitor.beans.QueryFiledEntry;

public class HqlHandler {
	
	public static String createHql(Object instance,List<QueryFiledEntry> queryfileds){
		return initHql(instance,queryfileds,1,null);
	}
	public static String createCountHql(Object instance,List<QueryFiledEntry> queryfileds){
		return initHql(instance,queryfileds,0,null);
	}
	public static String createHql(Object instance,List<QueryFiledEntry> queryfileds,String[][] orders){
		return initHql(instance,queryfileds,1,orders);
	}
	public static String createCountHql(Object instance,List<QueryFiledEntry> queryfileds,String[][] orders){
		return initHql(instance,queryfileds,0,orders);
	}
	
	public static String createGroupHql(Object instance,String selectSub,String[] groupField, int type, String[][] orders){
	    String classname = instance.getClass().getName();
        String aliasname = "obj";
        StringBuffer sb = new StringBuffer();
        if(type==1){
            sb.append("select "+selectSub+" From "+classname+" as "+aliasname+" ");
        }else{
            sb.append("select count("+aliasname+") From "+classname+" as "+aliasname+" ");
        }
        StringBuffer hqlconds = new StringBuffer(" where 1=1 ");
        StringBuffer hqlobjes = new StringBuffer();
        
        //拼装实体对象
        initcondsByInstance(hqlconds,hqlobjes,instance,aliasname);
        
        StringBuffer groupHql = new StringBuffer("");
        if(groupField!=null){
            groupHql = new StringBuffer(" group by ");
            for (int i = 0; i < groupField.length; i++) {
                String str = groupField[i];
                groupHql.append(aliasname + "." + str);
                if (i < groupField.length - 1)
                    groupHql.append(",");
            }
        }
        
        StringBuffer orderHql = new StringBuffer("");
        if (orders != null) {
            orderHql = new StringBuffer(" order by ");
            for (int i = 0; i < orders.length; i++) {
                String[] str = orders[i];
                orderHql.append(str[0] + " " + str[1]);
                if (i < orders.length - 1)
                    orderHql.append(",");
            }
        }
        
        return sb.append(hqlobjes).append(hqlconds).append(groupHql).append(orderHql).toString();
	}
	
	private static String initHql(Object instance,List<QueryFiledEntry> queryfileds,int type,String[][] orders){
		String classname = instance.getClass().getName();
		String aliasname = "obj";
		StringBuffer sb = new StringBuffer();
		if(type==1){
			sb.append("select "+aliasname+" From "+classname+" as "+aliasname+" ");
		}else{
			sb.append("select count("+aliasname+") From "+classname+" as "+aliasname+" ");
		}
		StringBuffer hqlconds = new StringBuffer(" where 1=1 ");
		StringBuffer hqlobjes = new StringBuffer();
		
		//拼装实体对象
		initcondsByInstance(hqlconds,hqlobjes,instance,aliasname);
		
		int objCount=0;
		QueryFiledEntry defineEntry = new QueryFiledEntry();
		//拼装拓展
		if(queryfileds!=null)
		for(QueryFiledEntry entry:queryfileds){
			if(entry.isIscol() && entry.getMatchvalue()!=null && entry.getMatchvalue().length>0){
				String[] temp = entry.getFieldname().split(",");
				if(temp.length>1){
					hqlobjes.append(" left join obj."+temp[0]+" "+temp[0]+"_"+objCount);
					QueryFiledEntry tempentry = entry;
					
					for(int i=0;i<temp.length-1;i++){
						String value = temp[i+1];
						if(value.indexOf(":")<0){
							tempentry.setFieldname(value);
							initconds(hqlconds,tempentry,temp[0]+"_"+objCount);
						}else{
							defineEntry.setFieldname(value.split(":")[0]);
							defineEntry.setMatchvalue(new String[]{value.split(":")[1]});
							initconds(hqlconds,defineEntry,temp[0]+"_"+objCount);
						}
					}
					objCount++;
				}
			}else{
				initconds(hqlconds,entry,aliasname);
			}
		}
		StringBuffer orderHql = new StringBuffer("");
		if (orders != null) {
			orderHql = new StringBuffer(" order by ");
			for (int i = 0; i < orders.length; i++) {
				String[] str = orders[i];
				orderHql.append(aliasname + "." + str[0] + " " + str[1]);
				if (i < orders.length - 1)
					orderHql.append(",");
			}
		}
		
		return sb.append(hqlobjes).append(hqlconds).append(orderHql).toString();
	}
	
	private static void initconds(StringBuffer hqlconds,QueryFiledEntry entry,String aliasname){
		if(entry!=null&&!StringUtils.isEmpty(entry.getFieldname())&&entry.getMatchvalue()!=null&&entry.getMatchvalue().length>0){
			if(entry.getExp()==QueryFiledEntry.QUERY_EXP_EQ){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{temp.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,"=",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LIKE){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{entry.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,"like",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LIKE_ANYWHERE){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{"%"+entry.getMatchvalue()[i]+"%"});
					hqlconds.append(initQueryFieldEntryConds(temp,"like",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LIKE_START){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{entry.getMatchvalue()[i]+"%"});
					hqlconds.append(initQueryFieldEntryConds(temp,"like",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LIKE_END){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{"%"+entry.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,"like",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_BETWEEN){
				if(entry.getMatchvalue().length==2 && !StringUtils.isEmpty(entry.getMatchvalue()[0]) && !StringUtils.isEmpty(entry.getMatchvalue()[1])){
					hqlconds.append(initQueryFieldEntryConds(entry,"between",false,aliasname));
					hqlconds.append(" "+initValue(entry.getMatchvalue()[0],entry.getDatavaluetype())+" and "+initValue(entry.getMatchvalue()[1],entry.getDatavaluetype()));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_IN){
				if(entry.getMatchvalue().length>0){
					hqlconds.append(initQueryFieldEntryConds(entry,"in",false,aliasname));
					hqlconds.append(" (");
					for(int i=0;i<entry.getMatchvalue().length;i++){
						hqlconds.append(initValue(entry.getMatchvalue()[i],entry.getDatavaluetype()));
						if(i<entry.getMatchvalue().length-1)
							hqlconds.append(",");
					}
					hqlconds.append(") ");
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LE){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{temp.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,"<=",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_LT){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{temp.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,"<",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_GE){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{temp.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,">=",true,aliasname));
				}
			}else if(entry.getExp()==QueryField.QUERY_EXP_GT){
				QueryFiledEntry temp = entry.clone();
				for(int i = 0;i<entry.getMatchvalue().length;i++){
					temp.setMatchvalue(new String[]{temp.getMatchvalue()[i]});
					hqlconds.append(initQueryFieldEntryConds(temp,">",true,aliasname));
				}
			}
		}
	}
	
	private static String toLower(String value){
		return value.toLowerCase();
	}
	
	private static String initQueryFieldEntryConds(QueryFiledEntry entry,String exp,boolean automatic,String aliasname){
		StringBuffer sb = new StringBuffer();
		sb.append(" "+entry.getRelstype());
		if(entry.getDatavaluetype()==AttributesType.TYPE_TIMEVALUE)
			sb.append(" Date("+aliasname+"."+entry.getFieldname()+")");
		else{
			if(entry.getDatavaluetype()==AttributesType.TYPE_STRINGVALUE){
				sb.append(" lower(");
				sb.append(" "+aliasname+"."+entry.getFieldname());
				sb.append(")");
			}else{
				sb.append(" "+aliasname+"."+entry.getFieldname());
			}
		}
		sb.append(" "+exp);
		
		sb.append(automatic?(" "+initValue(entry.getMatchvalue()[0],entry.getDatavaluetype())):"");
		return sb.toString();
	}
	
	private static String initValue(String value,Short type){
		if(AttributesType.TYPE_NUMBERVALUE==type || AttributesType.TYPE_FLOATVALUE==type){
			return value;
		}else{
			return toLower(" '"+value+"' ");
		}
	}
	
	private static String initValue(Object value){
		if(Date.class.isInstance(value)){
			return " '"+value+"' ";
		}
		if(String.class.isInstance(value)){
			return " '"+toLower(value.toString())+"' ";
		}
		return value.toString();
	}
	private static String initObjectConds(String fieldname,Object value,String aliasname,String exp,boolean automatic){
		StringBuffer sb = new StringBuffer();
		sb.append(" and");
		if(Date.class.isInstance(value)){
			sb.append(" Date("+aliasname+"."+fieldname+")");
		}else if(String.class.isInstance(value) && !fieldname.equals("attributevalue")){
			sb.append(" lower("+aliasname+"."+fieldname+")");
		}else{
			sb.append(" "+aliasname+"."+fieldname);
		}
		sb.append(" "+exp);
		if(Date.class.isInstance(value)){
			sb.append(automatic?(" "+initValue(DateFormatUtil.dateToString((Date)value, DateFormatUtil.SHROT_DATE))):"");
		}else{
			sb.append(automatic?(" "+initValue(value)):"");
		}
		return sb.toString();
	}
	
	private static void initcondsByInstance(StringBuffer hqlconds,StringBuffer hqlobjs,Object obj,String aliasname){
		Class<?> cls = obj.getClass();
		Method[] methods = cls.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method m = methods[i];
			try {
				if(m.isAnnotationPresent(QueryEntry.class)){
					Object value = m.invoke(obj);
					if(value!=null){
						QueryEntry qe = m.getAnnotation(QueryEntry.class);
						initcondsByInstance(hqlconds,hqlobjs,value,aliasname+"."+qe.name());
					}
				}else if(m.isAnnotationPresent(QueryField.class)){
					Object value = m.invoke(obj);
					if(value!=null&&!StringUtils.isEmpty(String.valueOf(value))){
						QueryField qf = m.getAnnotation(QueryField.class);
						if(qf.exp()==QueryField.QUERY_EXP_EQ){
							hqlconds.append(initObjectConds(qf.name(),value,aliasname,"=",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_LIKE){
							hqlconds.append(initObjectConds(qf.name(),value,aliasname,"like",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_LIKE_ANYWHERE){
							hqlconds.append(initObjectConds(qf.name(),"%"+value+"%",aliasname,"like",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_LIKE_START){
							hqlconds.append(initObjectConds(qf.name(),"%"+value,aliasname,"like",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_LIKE_END){
							hqlconds.append(initObjectConds(qf.name(),value+"%",aliasname,"like",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_BETWEEN){
							Object[] objs = (Object[])value;
							if(objs.length==2){
								hqlconds.append(initObjectConds(qf.name(),value,aliasname,"between",false));
								hqlconds.append(" "+initValue(objs[0])+" and "+initValue(objs[1]));
							}
						}else if(qf.exp()==QueryField.QUERY_EXP_IN){
						    Object[] objs = (Object[])value;
                            if(objs!=null && objs.length>0){
                                hqlconds.append(initObjectConds(qf.name(),value,aliasname,"in",false));
                                hqlconds.append(" (");
                                for(int k=0;k<objs.length;k++){
									if(objs[k]==null){
										continue;
									}
                                    hqlconds.append(initValue(objs[k]));
                                    if(k<objs.length-1){
                                        hqlconds.append(",");
                                    }
                                }
                                hqlconds.append(") ");
							}
						}else if(qf.exp()==QueryField.QUERY_EXP_LE){
							hqlconds.append(initObjectConds(qf.name(),"%"+value,aliasname,"<=",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_LT){
							hqlconds.append(initObjectConds(qf.name(),"%"+value,aliasname,"<",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_GE){
							hqlconds.append(initObjectConds(qf.name(),"%"+value,aliasname,">=",true));
						}else if(qf.exp()==QueryField.QUERY_EXP_GT){
							hqlconds.append(initObjectConds(qf.name(),"%"+value,aliasname,">",true));
						}
					}
				}else if(m.isAnnotationPresent(QueryCollection.class)){
					QueryCollection qc = m.getAnnotation(QueryCollection.class);
					Object value = m.invoke(obj);
					if(value!=null){
						Collection<?> collection = (Collection<?>)value;
						Iterator<?> iter = collection.iterator();
						int colcount = 0;
						while(iter.hasNext()){
							Object temp = iter.next();
							initObjHql(hqlobjs,aliasname,qc.name(),colcount,i);
							initcondsByInstance(hqlconds,hqlobjs,temp,qc.name()+i+"_"+colcount);
							colcount++;
						}
					}
				}
			}catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
	
	private static void initObjHql(StringBuffer hqlobjs,String aliasname,String objname,int colcount,int i){
		hqlobjs.append(" left join "+aliasname+"."+objname+" as "+objname+i+"_"+colcount);
	}
}


